package cn.stylefeng.guns.cloud.workflow.modular.service.impl;

import cn.hutool.core.io.IoUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.stylefeng.guns.cloud.libs.mp.page.PageFactory;
import cn.stylefeng.guns.cloud.model.page.PageResult;
import cn.stylefeng.guns.cloud.workflow.core.consts.ActConst;
import cn.stylefeng.guns.cloud.workflow.core.util.*;
import cn.stylefeng.guns.cloud.workflow.modular.model.params.DeployProcessParam;
import cn.stylefeng.guns.cloud.workflow.modular.model.result.DeployProcessResult;
import cn.stylefeng.guns.cloud.workflow.modular.service.DeployProcessService;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode;
import org.activiti.bpmn.converter.BpmnXMLConverter;
import org.activiti.bpmn.model.BpmnModel;
import org.activiti.editor.constants.ModelDataJsonConstants;
import org.activiti.editor.language.json.converter.BpmnJsonConverter;
import org.activiti.engine.RepositoryService;
import org.activiti.engine.repository.Model;
import org.activiti.engine.repository.ProcessDefinition;
import org.activiti.engine.repository.ProcessDefinitionQuery;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletResponse;
import javax.xml.stream.XMLInputFactory;
import javax.xml.stream.XMLStreamException;
import javax.xml.stream.XMLStreamReader;
import java.io.*;
import java.util.ArrayList;
import java.util.List;

import static cn.stylefeng.guns.cloud.workflow.core.consts.ActConst.FILE_ACTIVITI_ZIP;

/**
 * 流程部署 服务实现类
 *
 * @Author xuyuxiang
 * @Date 2019/11/14 15:00
 **/
@Service
public class DeployProcessServiceImpl implements DeployProcessService {

    private final String LIKE_DIVISION = "%";
    private final String BPMN_TYPE = "bpmn";
    private final String BPMN_SUFFIX = ".bpmn20.xml";
    private final String JSON_TYPE = "json";
    private final String JSON_SUFFIX = ".json";
    private final String EMPTY_PROCESS_NAME = "无流程名称";
    private final String MAPPING_NAME_SUFFIX = "【反射】";

    @Autowired
    private RepositoryService repositoryService;

    /**
     * 获取已部署的流程列表
     *
     * @Author xuyuxiang
     * @Date 2019/11/14 15:02
     **/
    public PageResult list(DeployProcessParam deployProcessParam, boolean isLatestVersion) {
        Page pageContext = getPageContext();

        ProcessDefinitionQuery processDefinitionQuery = repositoryService
                .createProcessDefinitionQuery();
        //设置查询参数
        if (ObjectUtil.isNotEmpty(deployProcessParam)) {

            String deploymentId = deployProcessParam.getDeploymentId();

            if (ObjectUtil.isNotEmpty(deploymentId)) {
                processDefinitionQuery.deploymentId(deploymentId);
            }

            String name = deployProcessParam.getName();

            if (ObjectUtil.isNotEmpty(name)) {
                processDefinitionQuery.processDefinitionNameLike(LIKE_DIVISION + name + LIKE_DIVISION);
            }

            String key = deployProcessParam.getKey();

            if (ObjectUtil.isNotEmpty(key)) {
                processDefinitionQuery.processDefinitionKeyLike(LIKE_DIVISION + key + LIKE_DIVISION);
            }
        }

        //最新版本
        if (isLatestVersion) {
            processDefinitionQuery.latestVersion();
        }

        //获取流程列表
        List<ProcessDefinition> processDefinitionList = processDefinitionQuery.listPage((int) pageContext.getSize() * ((int) pageContext.getCurrent() - 1),
                (int) pageContext.getSize());

        List<DeployProcessResult> list = new ArrayList<>();
        processDefinitionList.forEach(processDefinition -> list.add(new DeployProcessResult(processDefinition)));
        //设置分页数据
        long count = processDefinitionQuery.count();
        pageContext.setRecords(list);

        pageContext.setTotal(count);

        return new PageResult(pageContext);
    }

    /**
     * 删除流程
     *
     * @Author xuyuxiang
     * @Date 2019/11/14 15:04
     **/
    public void delete(String deploymentId) {
        repositoryService.deleteDeployment(deploymentId, true);
    }

    /**
     * 查看已部署模型流程图
     *
     * @Author xuyuxiang
     * @Date 2019/10/29 14:40
     **/
    public void viewPic(DeployProcessParam deployProcessParam, HttpServletResponse response) {

        String deploymentId = deployProcessParam.getDeploymentId();

        String imageName = deployProcessParam.getImageName();

        InputStream in = repositoryService.getResourceAsStream(deploymentId, imageName);
        try {
            response.setContentType("image/png");
            OutputStream out = response.getOutputStream();
            IoUtil.copy(in, out);
            // 关闭流
            out.close();
            in.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }


    /**
     * 获取pageContext
     *
     * @Author xuyuxiang
     * @Date 2019/10/25 16:04
     **/
    private Page getPageContext() {
        return PageFactory.defaultPage();
    }


    /**
     * 导出流程xml或json文件
     *
     * @Author xuyuxiang
     * @Date 2019/11/8 14:36
     **/
    public void export(DeployProcessParam deployProcessParam, HttpServletResponse response) {

        String deploymentId = deployProcessParam.getDeploymentId();

        String exportFileType = deployProcessParam.getExportFileType();

        OutputStream out = null;
        try {
            Model model = repositoryService.createModelQuery().deploymentId(deploymentId).singleResult();
            String modelId = model.getId();
            BpmnJsonConverter jsonConverter = new BpmnJsonConverter();

            byte[] modelEditorSource = repositoryService.getModelEditorSource(modelId);

            JsonNode editorNode = new ObjectMapper().readTree(modelEditorSource);

            BpmnModel bpmnModel = jsonConverter.convertToBpmnModel(editorNode);
            // 处理异常
            if (bpmnModel.getMainProcess() == null) {

                response.setStatus(HttpStatus.UNPROCESSABLE_ENTITY.value());

                response.getOutputStream().println("no main process, can't export for type: " + exportFileType);

                response.flushBuffer();

                return;
            }

            String filename = "";

            byte[] exportBytes = null;

            String mainProcessId = bpmnModel.getMainProcess().getId();

            if (exportFileType.equals(BPMN_TYPE)) {

                BpmnXMLConverter xmlConverter = new BpmnXMLConverter();

                exportBytes = xmlConverter.convertToXML(bpmnModel);

                filename = mainProcessId + BPMN_SUFFIX;

            } else if (exportFileType.equals(JSON_TYPE)) {

                exportBytes = modelEditorSource;

                filename = mainProcessId + JSON_SUFFIX;
            }
            out = response.getOutputStream();

            response.setContentType("application/octet-stream; charset=utf-8");

            response.setHeader("Content-Disposition", "attachment; filename=" + filename);

            response.flushBuffer();

            if (exportBytes != null) {

                out.write(exportBytes);
            }
        } catch (IOException e) {

            e.printStackTrace();

        } finally {

            IoUtil.close(out);
        }
    }

    /**
     * 挂起
     *
     * @Author xuyuxiang
     * @Date 2019/11/17 16:32
     **/
    @Override
    public void suspend(String processDefinitionId) {
        repositoryService.suspendProcessDefinitionById(processDefinitionId);
    }

    /**
     * 激活
     *
     * @Author xuyuxiang
     * @Date 2019/11/17 16:33
     **/
    @Override
    public void active(String processDefinitionId) {
        repositoryService.activateProcessDefinitionById(processDefinitionId);
    }

    /**
     * 打包下载
     *
     * @Author xuyuxiang
     * @Date 2019/11/19 11:42
     **/
    @Override
    public void downloadZip(String deploymentId, HttpServletResponse response) throws IOException {
        //生成先清空之前生成的文件
        DelFileUtil.delFolder(PathUtil.getProjectpath() + ActConst.FILE_ACTIVITI);

        List<String> names = repositoryService.getDeploymentResourceNames(deploymentId);
        for (String name : names) {
            if (name.contains("zip")) {
                continue;
            }
            InputStream in = repositoryService.getResourceAsStream(deploymentId, name);

            //把文件上传到文件目录里面
            try {
                FileUpload.copyFile(in, PathUtil.getProjectpath() + ActConst.FILE_ACTIVITI, name);
            } catch (IOException e) {
                e.printStackTrace();
            }
            in.close();
        }

        //生成的全部代码压缩成zip文件
        try {
            if (FileZip.zip(PathUtil.getProjectpath() + ActConst.FILE_ACTIVITI,
                    PathUtil.getProjectpath() + FILE_ACTIVITI_ZIP)) {

                //下载代码
                FileDownload.fileDownload(response, PathUtil.getProjectpath() + FILE_ACTIVITI_ZIP, "activitiFile.zip");
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 导入流程资源
     *
     * @Author xuyuxiang
     * @Date 2019/11/19 11:46
     **/
    @Override
    public void importProcess(MultipartFile file) {
        //TODO
        /*//bpmn文件上传路径
        String fileParentPath = ConstantsContext.getBpmnFileUploadPath();
        //执行上传
        UploadResult uploadResult = fileInfoService.uploadFile(file, fileParentPath);

        try {

            String originalFilename = uploadResult.getOriginalFilename();

            String fileSavePath = uploadResult.getFileSavePath();

            if (ObjectUtil.isEmpty(originalFilename)) {
                originalFilename = EMPTY_PROCESS_NAME;
            }

            //末尾需要加上.bpmn20.xml
            originalFilename = originalFilename + BPMN_SUFFIX;

            InputStream inputStream = new FileInputStream(fileSavePath);

            //创建部署对象
            DeploymentBuilder deploymentBuilder = repositoryService.createDeployment();

            //部署名称
            deploymentBuilder.name(originalFilename);
            deploymentBuilder.addInputStream(originalFilename, inputStream);
            //完成部署
            deploymentBuilder.deploy();
            inputStream.close();
        } catch (Exception e) {
            e.printStackTrace();
        }*/
    }

    /**
     * 映射模型
     *
     * @Author xuyuxiang
     * @Date 2019/11/19 11:51
     **/
    @Override
    public void mapping(String processDefinitionId) throws UnsupportedEncodingException, XMLStreamException {
        ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery()
                .processDefinitionId(processDefinitionId).singleResult();

        InputStream bpmnStream = repositoryService.getResourceAsStream(processDefinition.getDeploymentId(),
                processDefinition.getResourceName());

        XMLInputFactory xif = XMLInputFactory.newInstance();

        InputStreamReader in = new InputStreamReader(bpmnStream, "UTF-8");

        XMLStreamReader xtr = xif.createXMLStreamReader(in);

        BpmnModel bpmnModel = new BpmnXMLConverter().convertToBpmnModel(xtr);

        BpmnJsonConverter converter = new BpmnJsonConverter();
        ObjectNode modelNode = converter.convertToJson(bpmnModel);
        Model modelData = repositoryService.newModel();

        //唯一标识
        modelData.setKey(processDefinition.getKey());

        //名称
        modelData.setName(processDefinition.getName() + MAPPING_NAME_SUFFIX);

        modelData.setDeploymentId(processDefinition.getDeploymentId());

        //版本
        modelData.setVersion(Integer.parseInt(String.valueOf(repositoryService.createModelQuery().modelKey(modelData.getKey()).count() + 1)));

        ObjectNode modelObjectNode = new ObjectMapper().createObjectNode();
        modelObjectNode.put(ModelDataJsonConstants.MODEL_NAME, processDefinition.getName());
        modelObjectNode.put(ModelDataJsonConstants.MODEL_REVISION, modelData.getVersion());
        modelObjectNode.put(ModelDataJsonConstants.MODEL_DESCRIPTION, processDefinition.getDescription());
        modelData.setMetaInfo(modelObjectNode.toString());

        //保存模型
        repositoryService.saveModel(modelData);

        //保存资源
        repositoryService.addModelEditorSource(modelData.getId(), modelNode.toString().getBytes("utf-8"));
    }
}
